package com.shen.shenproject.downfile

import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.shen.shenproject.R
import com.shen.shenproject.utils.SdkVersionUtils
import okio.BufferedSink
import okio.BufferedSource
import okio.Okio
import java.io.Closeable
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*


class DownFileActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_down_file)


        // https://www.xbzl.cc/resource/videos/1618567259736.mp4


        findViewById<Button>(R.id.btn_down).setOnClickListener {
            showLoadingImage("https://www.xbzl.cc/resource/videos/1618567259736.mp4")
        }
    }

    // 下载图片保存至手机
    fun showLoadingImage(urlPath: String?): String? {
        var outImageUri: Uri? = null
        var outputStream: OutputStream? = null
        var inputStream: InputStream? = null
        var inBuffer: BufferedSource? = null
        try {
            if (SdkVersionUtils.checkedAndroid_Q()) {
                outImageUri = createOutVideoUri()

            } else {
                val suffix: String = getLastVideoSuffix(MIME_TYPE_MP4)
                val state = Environment.getExternalStorageState()

                val rootDir = if (state == Environment.MEDIA_MOUNTED)
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
                else getExternalFilesDir(Environment.DIRECTORY_MOVIES)
                if (rootDir != null) {
                    if (!rootDir.exists()) {
                        rootDir.mkdirs()
                    }
                    val folderDir = File(if (state != Environment.MEDIA_MOUNTED) rootDir.absolutePath
                    else rootDir.absolutePath + File.separator + CAMERA + File.separator)
                    if (!folderDir.exists() && folderDir.mkdirs()) { }
                    val fileName: String = getCreateFileName("VIDEO_").toString() + suffix
                    val file = File(folderDir, fileName)

                    outImageUri = Uri.fromFile(file)
                }
            }
            if (outImageUri != null) {
                outputStream = Objects.requireNonNull(contentResolver.openOutputStream(outImageUri))
                val u = URL(urlPath)
                inputStream = u.openStream()
                inBuffer = Okio.buffer(Okio.source(inputStream))


                val bufferCopy: Boolean = bufferCopy(inBuffer, outputStream)
                if (bufferCopy) {
                    return getPath(this, outImageUri)
                }
            }
        } catch (e: Exception) {
            if (outImageUri != null && SdkVersionUtils.checkedAndroid_Q()) {
                contentResolver.delete(outImageUri, null, null)
            }
        } finally {
            close(inputStream)
            close(outputStream)
            close(inBuffer)
        }
        return null
    }

    private val MIME_TYPE_PNG = "image/png"
    private val MIME_TYPE_JPEG = "image/jpeg"
    private val MIME_TYPE_JPG = "image/jpg"
    private val MIME_TYPE_BMP = "image/bmp"
    private val MIME_TYPE_GIF = "image/gif"
    private val MIME_TYPE_WEBP = "image/webp"

    private val MIME_TYPE_3GP = "video/3gp"
    private val MIME_TYPE_MP4 = "video/mp4"
    private val MIME_TYPE_MPEG = "video/mpeg"
    private val MIME_TYPE_AVI = "video/avi"

    private val DCIM = "DCIM/Camera"
    private val CAMERA = "Camera"

    private val JPEG = ".jpeg"
    private val PNG = ".png"
    private val MP4 = ".mp4"

    private val JPEG_Q = "image/jpeg"
    private val PNG_Q = "image/png"
    private val MP4_Q = "video/mp4"
    private val AVI_Q = "video/avi"

    /**
     * 针对Q版本创建uri
     *
     * @return
     */
    private fun createOutImageUri(): Uri? {
        val contentValues = ContentValues()
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, getCreateFileName("IMG_"))
        contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis().toString())
        contentValues.put(MediaStore.Images.Media.MIME_TYPE, MIME_TYPE_PNG)
        contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, DCIM)
        return contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
    }


    /**
     * 针对Q版本创建uri
     *
     * @return
     */
    private fun createOutVideoUri(): Uri? {
        val contentValues = ContentValues()
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, getCreateFileName("VIDEO_"))
        contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis().toString())
        contentValues.put(MediaStore.Images.Media.MIME_TYPE, MIME_TYPE_MP4)
        contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, DCIM)
        return contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
    }


    /**
     * 根据时间戳创建文件名
     *
     * @param prefix 前缀名
     * @return
     */
   private fun getCreateFileName(prefix: String): String? {
        val millis = System.currentTimeMillis()
        val sf = SimpleDateFormat("yyyyMMdd_HHmmssSS")
        return prefix + sf.format(millis)
    }


    /**
     * Get image suffix
     *
     * @param mineType
     * @return
     */
    fun getLastImgSuffix(mineType: String): String {
        val defaultSuffix: String = PNG
        try {
            val index = mineType.lastIndexOf("/") + 1
            if (index > 0) {
                return "." + mineType.substring(index)
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            return defaultSuffix
        }
        return defaultSuffix
    }

    /**
     * Get image suffix
     *
     * @param mineType
     * @return
     */
    fun getLastVideoSuffix(mineType: String): String {
        val defaultSuffix: String = MP4
        try {
            val index = mineType.lastIndexOf("/") + 1
            if (index > 0) {
                return "." + mineType.substring(index)
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            return defaultSuffix
        }
        return defaultSuffix
    }



    /**
     * 拷贝文件
     *
     * @param outputStream
     * @return
     */
    fun bufferCopy(inBuffer: BufferedSource?, outputStream: OutputStream?): Boolean {
        var outBuffer: BufferedSink? = null
        try {
            outBuffer = Okio.buffer(Okio.sink(outputStream))
            outBuffer.writeAll(inBuffer)
            outBuffer.flush()
            return true
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        } finally {
            close(inBuffer)
            close(outBuffer)
        }
        return false
    }

    fun close(c: Closeable?) {
        // java.lang.IncompatibleClassChangeError: interface not implemented
        if (c != null && c is Closeable) {
            try {
                c.close()
            } catch (e: java.lang.Exception) {
                // silence
            }
        }
    }


    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.<br></br>
     * <br></br>
     * Callers should check whether the path is local before assuming it
     * represents a local file.
     *
     * @param context The context.
     * @param uri     The Uri to query.
     * @author paulburke
     */
    @SuppressLint("NewApi")
    fun getPath(ctx: Context, uri: Uri): String? {
        val context = ctx.applicationContext
        val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            if (isExternalStorageDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                if ("primary".equals(type, ignoreCase = true)) {
                    return if (SdkVersionUtils.checkedAndroid_Q()) {
                        context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
                            .toString() + "/" + split[1]
                    } else {
                        Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                    }
                }

                // TODO handle non-primary volumes
            } else if (isDownloadsDocument(uri)) {
                val id = DocumentsContract.getDocumentId(uri)
                val contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
                )
                return getDataColumn(context, contentUri, null, null)
            } else if (isMediaDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                var contentUri: Uri? = null
                if ("image" == type) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                } else if ("video" == type) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else if ("audio" == type) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                }
                val selection = "_id=?"
                val selectionArgs = arrayOf(split[1])
                return getDataColumn(context, contentUri, selection, selectionArgs)
            }
        } else if ("content".equals(uri.scheme, ignoreCase = true)) {

            // Return the remote address
            return if (isGooglePhotosUri(uri)) uri.lastPathSegment
                else getDataColumn(context, uri, null, null)
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
            return uri.path
        }
        return null
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     * @author paulburke
     */
    fun isExternalStorageDocument(uri: Uri): Boolean {
        return "com.android.externalstorage.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     * @author paulburke
     */
    fun isDownloadsDocument(uri: Uri): Boolean {
        return "com.android.providers.downloads.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     * @author paulburke
     */
    fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    fun isGooglePhotosUri(uri: Uri): Boolean {
        return "com.google.android.apps.photos.content" == uri.authority
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context       The context.
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     * @author paulburke
     */
    fun getDataColumn(
        context: Context, uri: Uri?, selection: String?,
        selectionArgs: Array<String>?
    ): String? {
        var cursor: Cursor? = null
        val column = "_data"
        val projection = arrayOf(
            column
        )
        try {
            cursor = context.contentResolver.query(
                uri!!, projection, selection, selectionArgs,
                null
            )
            if (cursor != null && cursor.moveToFirst()) {
                val column_index = cursor.getColumnIndexOrThrow(column)
                return cursor.getString(column_index)
            }
        } catch (ex: IllegalArgumentException) {
            Log.i("shen",
                String.format(Locale.getDefault(), "getDataColumn: _data - [%s]", ex.message)
            )
        } finally {
            cursor?.close()
        }
        return null
    }

}
